#pragma once

#include <pthread.h>
#include <vector>
#include <semaphore.h>
#include <cassert>

template <class T>
class RingQueue
{
public:
    RingQueue(int cap = 5)
        : _queue(cap), _cap(cap), _productorPos(0), _consumerPos(0)
    {
        int n = sem_init(&_spaceSem, 0, _cap);
        assert(n == 0);
        n = sem_init(&_dataSem, 0, 0);
        assert(n == 0);

        pthread_mutex_init(&_pmutex, nullptr);
        pthread_mutex_init(&_cmutex, nullptr);
    }

    // P操作，申请资源
    void P(sem_t &sem)
    {
        int n = sem_wait(&sem);
        assert(n == 0);
        (void)n;
    }

    // V操作，归还资源
    void V(sem_t &sem)
    {
        int n = sem_post(&sem);
        assert(n == 0);
        (void)n;
    }

    // 生产者
    void push(const T &in)
    {
        //这里，先申请信号量，再竞争锁。---多个线程已申请完信号量，一个线程申请到锁
        //顺序反过来---------------------一个线程申请到锁，这一个线程申请信号量。  效下降
        P(_spaceSem);
        pthread_mutex_lock(&_pmutex);
        // 在生产者的位置放入数据
        _queue[_productorPos++] = in;
        _productorPos %= _cap; // 维持环形队列

        pthread_mutex_unlock(&_pmutex);
        V(_dataSem); // 放入数据没相当于归还数据资源
    }

    // 消费者
    void pop(T *out)
    {
        P(_dataSem);
        pthread_mutex_lock(&_cmutex);
        *out = _queue[_consumerPos++];
        _consumerPos %= _cap;

        pthread_mutex_unlock(&_cmutex);
        V(_spaceSem);
    }

    ~RingQueue()
    {
        sem_destroy(&_spaceSem);
        sem_destroy(&_dataSem);
        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }

private:
    std::vector<T> _queue; // 用数组模拟队列
    int _cap;
    sem_t _spaceSem;   // 生产者的信号量，生产者需要空间资源
    sem_t _dataSem;    // 消费者的信号量，消费者需要数据资源
    int _productorPos; // 生产者的位置，下标
    int _consumerPos;  // 消费者的位置，下标

    pthread_mutex_t _pmutex;
    pthread_mutex_t _cmutex;
};
